/**
 * @file utime64.c  
 * @author xy
 * @brief 
 * @version 0.1
 * @date 2024-12-23
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#include "utime64.h"

#define IS_LEAP_YEAR(y) (((((y)%4) == 0) && (((y)%100) != 0)) || (((y)%400) == 0))

#define SECONDS_PER_DAY (60 * 60 * 24)

const int month_days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
const int month_days_leap[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};


const int days_per_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int days_per_month_leap[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

time64_t tm_to_time64(struct utime_s *tm)
{
    time64_t days = 0;
    time64_t i = 0;
    time64_t yc400 = (tm->tm_year - 1970) / 400;

    if (tm->tm_year < 1970)
    {
        return 0;
    }

    days += yc400 * 146097;

    for (i = (1970 + yc400 * 400); i < tm->tm_year; i++)
    {
        if (IS_LEAP_YEAR(i))
        {
            days += 366;
        } else
        {
            days += 365;
        }
    }

    if (IS_LEAP_YEAR(tm->tm_year))
    {
        days += month_days_leap[tm->tm_mon - 1];
    } else
    {
        days += month_days[tm->tm_mon - 1];
    }
    days += tm->tm_mday - 1;


    return days * SECONDS_PER_DAY + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
}

int time64_to_tm(struct utime_s *tm, time64_t t)
{
    time64_t days = 0;
    time64_t secs = 0;

    if (t < 0)
    {
        return -1;
    }

    days = t / (60 * 60 * 24);
    secs = t % (60 * 60 * 24);

    tm->tm_sec = secs % 60;
    tm->tm_min = (secs / 60) % 60;
    tm->tm_hour = secs / 3600;
    tm->tm_mon = 1;
    tm->tm_mday = 1;

    time64_t yc400 = days / 146097;
    days -= yc400 * 146097;

    tm->tm_year = yc400 * 400 + 1970;


    while (1)
    {
        if (IS_LEAP_YEAR(tm->tm_year))
        {
            if (days < 366)
            {
                break;
            } else
            {
                days -= 366;
                tm->tm_year++;
            }
        } else
        {
            if (days < 365)
            {
                break;
            } else
            {
                days -= 365;
                tm->tm_year++;
            }
        }
    }

    while (1)
    {
        if (IS_LEAP_YEAR(tm->tm_year))
        {
            if (days < days_per_month_leap[tm->tm_mon])
            {
                break;
            } else
            {
                days -= days_per_month_leap[tm->tm_mon];
                tm->tm_mon++;
            }
        } else
        {
            if (days < days_per_month[tm->tm_mon])
            {
                break;
            } else
            {
                days -= days_per_month[tm->tm_mon];
                tm->tm_mon++;
            }
        }
    }

    tm->tm_mday = days + 1;

    return 0;
}
